/*
*   scheduler.c
*   Authors: Dustin Striplin, Bill Minter, Wilson Burhan
*   the simulator, contains the ui & is updated in real time when an event occurs.
*/

#include <pthread.h>
#include <time.h>
#include "scheduler.h"
pthread_mutex_t updateMTX; //use for updating the gui
/*
*   Update the gui with the contents of the packet
*/
void updateGui(PacketPtr packet) {
    if(packet != (PacketPtr) NULL) {
        //get update mutex lock so more than one thread won't try updating at the same time.
        pthread_mutex_lock(&updateMTX);
        //update gui
        printf("type = %d\n", packet->type);
        printf("requestID = %d\n", packet->requestID);
        printf("currently running = %d\n", packet->runningProcess);

        int j = 0;
        printf("Ready Queue: ");  //print ready queue

        for(; j < packet->readyQueueSize; j++) {
            printf("%d, ", packet->readyQueue[j]);
        }
        printf("\n");

        int i = 0;
        for(; i < NUM_IO; i++) {  //print devies
            int index = 0;
            int id = packet->deviceQueues[i][index];
            printf("Device# %d: ",i);
            while(id != (int) NULL && id != NO_ID) {
                printf("%d, ", id);
                index++;
                id = packet->deviceQueues[i][index];
            }
            printf("\n");
        }

        //print mutexes
        i = 0;
        for(; i < packet->numProducerConsumer; i++) {
            int index = 0;
            int id = packet->mutexQueues[i][index];
            printf("Mutex# %d, owner %d:  ", i, packet->mutexOwners[i]);
            while(id != (int) NULL && id != NO_ID) {
                printf("%d, ", id);
                index++;
                id = packet->mutexQueues[i][index];
            }
            printf("\n");
        }

        //print condition variables
        i = 0;
        for(; i < packet->numProducerConsumer*2; i++) {
            int index = 0;
            int id = packet->conditionQueues[i][index];
            printf("CV# %d: ", i);
            while(id != (int) NULL && id != NO_ID) {
                printf("%d, ", id);
                index++;
                id = packet->conditionQueues[i][index];
            }
            printf("\n");
        }

        if(packet->consumersValue != NO_ID)
            printf("shared value = %d\n", packet->consumersValue);

        printf("\n");

        pthread_mutex_unlock(&updateMTX);//release update mutex lock

        packet->destruct(packet);//destroy the packet
    }
}

//has a loop that calls cpuStep, cpuStep will return a packet that we use to update the gui
//arguments are in the form of -cb 8 -pc 4
int main(int argc, char*argv[]) {
    //////////////////////////////PARSE ARGUMENTS///////////////////////////////////////
    long numCompute, numProdCon, numCycle=0;
    numCompute = strtol(argv[2], NULL, 0);
    numProdCon = strtol(argv[4], NULL, 0);
    numCycle = strtol(argv[6], NULL, 0);

    if (numCycle == 0)
        numCycle = 10000;

    ////////////////////////INITIALIZE PROGRAM/////////////////////////////////////////////////

    pthread_mutex_init(&updateMTX, NULL);//initialize ready queue's mutex
    srand(time(NULL));//seed random number generator
    PacketPtr packet; //for storing meta data about the last thing that ran.
    unsigned int count = 0; //the number of times the loop has run
    unsigned int current_step = 0;//the current number of time ticks
    int numThreads = NUM_IO + 1; //IO threads, 1 timer thread, 1 keyboard thread
    CPUPtr cpu = cpuConstructor(numCompute,numProdCon, updateGui);



    ////////////////////////////RUN THE PROGRAM//////////////////////////////////////////////////
    //while(1) {
    while(current_step < numCycle) {
        if(count % MOD_NUM == 0) {
            PacketPtr newPacket = cpu->step(cpu);
            if(newPacket != (PacketPtr)NULL)     //a null packet means there is nothing to update
                updateGui(newPacket);
            current_step++;
        }
        count++;
    }


    ////////////////////////////CLEAN UP FOR ENDING PROGRAM///////////////////////////////////////
    cpu->isRunning = FALSE;  //turn on cpu so the threads will finish

    wakeIOThreads(cpu);//wake all threads that may have been waiting on conditions.
                        //do this after setting isRunning to false, so the threads don't
                        //try to do more work.

    //join all threads so the cpu can be destructed
    int i = 0;
    int errorCode = 0;
    for(;i < numThreads; i++) {
        errorCode = pthread_join(cpu->threads[i], NULL);
        if(errorCode)
            printf("Error joining pthread %d, error code = %d\n", i, errorCode);
    }

    cpu->destruct(cpu);
}
